<template>
  <!-- 'uni-forms-item--border', -->
  <view
    class="uni-forms-item"
    :class="['is-direction-' + localLabelPos, isFirstBorder ? 'is-first-border' : '']"
  >
    <slot name="label">
      <view
        class="uni-forms-item__label break-all"
        :class="{ 'no-label': !label && !isRequired }"
        :style="{
          width: localLabelPos === 'left' ? localLabelWidth : 'auto',
          justifyContent: localLabelAlign,
        }"
      >
        <text v-if="isRequired" class="is-required">*</text>
        <text>{{ label }}</text>
        <text
          v-if="tip.showTip"
          class="ali-icon icon-question_fill ml-8 fz-20"
          style="color: #969799"
          @click="showTip"
        ></text>
      </view>
    </slot>
    <!-- #ifndef APP-NVUE -->
    <view
      class="uni-forms-item__content"
      :class="[contentRight && localLabelPos === 'left' ? 'content-right' : '']"
    >
      <slot></slot>
      <view class="uni-forms-item__error" :class="{ 'msg--active': msg }">
        <text>{{ msg }}</text>
      </view>
    </view>
    <!-- #endif -->
    <!-- #ifdef APP-NVUE -->
    <view
      class="uni-forms-item__nuve-content"
      :class="[contentRight && localLabelPos === 'left' ? 'content-right' : '']"
    >
      <view class="uni-forms-item__content">
        <slot></slot>
      </view>
      <view class="uni-forms-item__error" :class="{ 'msg--active': msg }">
        <text class="error-text">{{ msg }}</text>
      </view>
    </view>
    <!-- #endif -->
  </view>
</template>

<script>
  import { getActualWidthOfChars } from '@/utils';
  /**
   * uni-fomrs-item 表单子组件
   * @description uni-fomrs-item 表单子组件，提供了基础布局已经校验能力
   * @tutorial https://ext.dcloud.net.cn/plugin?id=2773
   * @property {Boolean} required 是否必填，左边显示红色"*"号
   * @property {String } 	label 				输入框左边的文字提示
   * @property {Number } 	labelWidth 			label的宽度，单位px（默认65）
   * @property {String } 	labelAlign = [left|center|right] label的文字对齐方式（默认left）
   * 	@value left		label 左侧显示
   * 	@value center	label 居中
   * 	@value right	label 右侧对齐
   * @property {String } 	errorMessage 		显示的错误提示内容，如果为空字符串或者false，则不显示错误信息
   * @property {String } 	name 				表单域的属性名，在使用校验规则时必填
   * @property {String } 	leftIcon 			【1.4.0废弃】label左边的图标，限 uni-ui 的图标名称
   * @property {String } 	iconColor 		【1.4.0废弃】左边通过icon配置的图标的颜色（默认#606266）
   * @property {String} validateTrigger = [bind|submit|blur]	【1.4.0废弃】校验触发器方式 默认 submit
   * 	@value bind 	发生变化时触发
   * 	@value submit 提交时触发
   * 	@value blur 	失去焦点触发
   * @property {String } 	labelPosition = [top|left] 【1.4.0废弃】label的文字的位置（默认left）
   * 	@value top	顶部显示 label
   * 	@value left	左侧显示 label
   */

  export default {
    name: 'uniFormsItem',
    options: {
      virtualHost: true,
    },
    provide() {
      return {
        uniFormItem: this,
      };
    },
    inject: {
      form: {
        from: 'uniForm',
        default: null,
      },
    },
    props: {
      // 表单校验规则
      rules: {
        type: Array,
        default() {
          return null;
        },
      },
      // 表单域的属性名，在使用校验规则时必填
      name: {
        type: [String, Array],
        default: '',
      },
      required: {
        type: Boolean,
        default: false,
      },
      label: {
        type: String,
        default: '',
      },
      // label的宽度 ，默认 80
      labelWidth: {
        type: [String, Number],
        default: '',
      },
      // label 居中方式，默认 left 取值 left/center/right
      labelAlign: {
        type: String,
        default: '',
      },
      // 强制显示错误信息
      errorMessage: {
        type: [String, Boolean],
        default: '',
      },
      // 1.4.0 弃用，统一使用 form 的校验时机
      // validateTrigger: {
      // 	type: String,
      // 	default: ''
      // },
      // 1.4.0 弃用，统一使用 form 的label 位置
      labelPosition: {
        type: String,
        default: '',
      },
      // 1.4.0 以下属性已经废弃，请使用  #label 插槽代替
      leftIcon: String,
      iconColor: {
        type: String,
        default: '#606266',
      },
      contentRight: {
        type: Boolean,
        default: false,
      },
      tip: {
        type: Object,
        default: () => ({
          showTip: false,
          title: '1',
          content: '2',
        }),
      },
    },
    data() {
      return {
        errMsg: '',
        isRequired: false,
        userRules: null,
        localLabelAlign: 'left',
        localLabelWidth: '65px',
        localLabelPos: 'left',
        border: false,
        isFirstBorder: false,
      };
    },
    computed: {
      // 处理错误信息
      msg() {
        return this.errorMessage || this.errMsg;
      },
    },
    watch: {
      // 规则发生变化通知子组件更新
      'form.formRules'(val) {
        // TODO 处理头条vue3 watch不生效的问题
        // #ifndef MP-TOUTIAO
        this.init();
        // #endif
      },
      'form.labelWidth'(val) {
        // 宽度
        this.localLabelWidth = this._labelWidthUnit(val);
      },
      'form.labelPosition'(val) {
        // 标签位置
        this.localLabelPos = this._labelPosition();
      },
      'form.labelAlign'(val) {},
      required(val) {
        this.isRequired = val;
      },
    },
    created() {
      this.init(true);
      if (this.name && this.form) {
        // TODO 处理头条vue3 watch不生效的问题
        // #ifdef MP-TOUTIAO
        this.$watch('form.formRules', () => {
          this.init();
        });
        // #endif

        // 监听变化
        this.$watch(
          () => {
            const val = this.form._getDataValue(this.name, this.form.localData);
            return val;
          },
          (value, oldVal) => {
            const isEqual = this.form._isEqual(value, oldVal);
            // 简单判断前后值的变化，只有发生变化才会发生校验
            // TODO  如果 oldVal = undefined ，那么大概率是源数据里没有值导致 ，这个情况不哦校验 ,可能不严谨 ，需要在做观察
            // fix by mehaotian 暂时取消 && oldVal !== undefined ，如果formData 中不存在，可能会不校验
            if (!isEqual) {
              const val = this.itemSetValue(value);
              this.onFieldChange(val, false);
            }
          },
          {
            immediate: false,
          },
        );
      }
    },
    // #ifndef VUE3
    destroyed() {
      if (this.__isUnmounted) return;
      this.unInit();
    },
    // #endif
    // #ifdef VUE3
    unmounted() {
      this.__isUnmounted = true;
      this.unInit();
    },
    // #endif
    methods: {
      showTip() {
        uni.showModal({
          title: this.tip.title,
          content: this.tip.content,
          showCancel: false,
          confirmText: '知道了',
        });
      },
      /**
       * 外部调用方法
       * 设置规则 ，主要用于小程序自定义检验规则
       * @param {Array} rules 规则源数据
       */
      setRules(rules = null) {
        this.userRules = rules;
        this.init(false);
      },
      // 兼容老版本表单组件
      setValue() {
        // console.log('setValue 方法已经弃用，请使用最新版本的 uni-forms 表单组件以及其他关联组件。');
      },
      /**
       * 外部调用方法
       * 校验数据
       * @param {any} value 需要校验的数据
       * @param {boolean} 是否立即校验
       * @return {Array|null} 校验内容
       */
      async onFieldChange(value, formtrigger = true) {
        const {
          formData,
          localData,
          errShowType,
          validateCheck,
          validateTrigger,
          _isRequiredField,
          _realName,
        } = this.form;
        const name = _realName(this.name);
        if (!value) {
          value = this.form.formData[name];
        }
        // fixd by mehaotian 不在校验前清空信息，解决闪屏的问题
        // this.errMsg = '';

        // fix by mehaotian 解决没有检验规则的情况下，抛出错误的问题
        const ruleLen = this.itemRules.rules && this.itemRules.rules.length;
        if (!this.validator || !ruleLen || ruleLen === 0) return;

        // 检验时机
        // let trigger = this.isTrigger(this.itemRules.validateTrigger, this.validateTrigger, validateTrigger);
        const isRequiredField = _isRequiredField(this.itemRules.rules || []);
        let result = null;
        // 只有等于 bind 时 ，才能开启时实校验
        if (validateTrigger === 'bind' || formtrigger) {
          // 校验当前表单项
          result = await this.validator.validateUpdate(
            {
              [name]: value,
            },
            formData,
          );

          // 判断是否必填,非必填，不填不校验，填写才校验 ,暂时只处理 undefined  和空的情况
          if (!isRequiredField && (value === undefined || value === '')) {
            result = null;
          }

          // 判断错误信息显示类型
          if (result && result.errorMessage) {
            if (errShowType === 'undertext') {
              // 获取错误信息
              this.errMsg = !result ? '' : result.errorMessage;
            }
            if (errShowType === 'toast') {
              uni.showToast({
                title: result.errorMessage || '校验错误',
                icon: 'none',
              });
            }
            if (errShowType === 'modal') {
              uni.showModal({
                title: '提示',
                content: result.errorMessage || '校验错误',
              });
            }
          } else {
            this.errMsg = '';
          }
          // 通知 form 组件更新事件
          validateCheck(result ? result : null);
        } else {
          this.errMsg = '';
        }
        return result ? result : null;
      },
      /**
       * 初始组件数据
       */
      init(type = false) {
        const {
          validator,
          formRules,
          childrens,
          formData,
          localData,
          _realName,
          labelWidth,
          _getDataValue,
          _setDataValue,
        } = this.form || {};
        // 对齐方式
        this.localLabelAlign = this._justifyContent();
        // 宽度
        this.localLabelWidth = this._labelWidthUnit(labelWidth);
        // 标签位置
        this.localLabelPos = this._labelPosition();
        this.isRequired = this.required;
        // 将需要校验的子组件加入form 队列
        this.form && type && childrens.push(this);

        if (!validator || !formRules) return;
        // 判断第一个 item
        if (!this.form.isFirstBorder) {
          this.form.isFirstBorder = true;
          this.isFirstBorder = true;
        }

        // 判断 group 里的第一个 item
        if (this.group) {
          if (!this.group.isFirstBorder) {
            this.group.isFirstBorder = true;
            this.isFirstBorder = true;
          }
        }
        this.border = this.form.border;
        // 获取子域的真实名称
        const name = _realName(this.name);
        const itemRule = this.userRules || this.rules;
        if (typeof formRules === 'object' && itemRule) {
          // 子规则替换父规则
          formRules[name] = {
            rules: itemRule,
          };
          validator.updateSchema(formRules);
        }
        // 注册校验规则
        const itemRules = formRules[name] || {};
        this.itemRules = itemRules;
        // 注册校验函数
        this.validator = validator;
        // 默认值赋予
        this.itemSetValue(_getDataValue(this.name, localData));
        this.isRequired = this.required;
      },
      unInit() {
        if (this.form) {
          const { childrens, formData, _realName } = this.form;
          childrens.forEach((item, index) => {
            if (item === this) {
              this.form.childrens.splice(index, 1);
              delete formData[_realName(item.name)];
            }
          });
        }
      },
      // 设置item 的值
      itemSetValue(value) {
        const name = this.form._realName(this.name);
        const rules = this.itemRules.rules || [];
        const val = this.form._getValue(name, value, rules);
        this.form._setDataValue(name, this.form.formData, val);
        return val;
      },

      /**
       * 移除该表单项的校验结果
       */
      clearValidate() {
        this.errMsg = '';
      },

      // 处理对齐方式
      _justifyContent() {
        if (this.form) {
          const { labelAlign } = this.form;
          let labelAli = this.labelAlign ? this.labelAlign : labelAlign;
          if (labelAli === 'left') return 'flex-start';
          if (labelAli === 'center') return 'center';
          if (labelAli === 'right') return 'flex-end';
        }
        return 'flex-start';
      },
      // 处理 label宽度单位 ,继承父元素的值
      _labelWidthUnit(labelWidth) {
        // if (this.form) {
        // 	const {
        // 		labelWidth
        // 	} = this.form
        if (this.labelPosition === 'top') return 'auto';
        return this.num2px(
          this.labelWidth ? this.labelWidth : labelWidth || (this.label ? 65 : 'auto'),
        );
        // }
        // return '65px'
      },
      // 处理 label 位置
      _labelPosition() {
        /** label宽度占屏幕宽度一半, 即强制为上下布局 */
        if (this.label.length > 10) {
          if (
            getActualWidthOfChars(this.label, '14px Microsoft YaHei') >
            (uni.getWindowInfo().windowWidth - uni.upx2px(60) - 12) / 2
          ) {
            return 'top';
          }
        }
        const defaultLabelPosition = 'left';
        if (this.labelPosition) return this.labelPosition;
        if (this.form) return this.form.labelPosition || defaultLabelPosition;
        return defaultLabelPosition;
      },

      /**
       * 触发时机
       * @param {Object} rule 当前规则内时机
       * @param {Object} itemRlue 当前组件时机
       * @param {Object} parentRule 父组件时机
       */
      isTrigger(rule, itemRlue, parentRule) {
        //  bind  submit
        if (rule === 'submit' || !rule) {
          if (rule === undefined) {
            if (itemRlue !== 'bind') {
              if (!itemRlue) {
                return parentRule === '' ? 'bind' : 'submit';
              }
              return 'submit';
            }
            return 'bind';
          }
          return 'submit';
        }
        return 'bind';
      },
      num2px(num) {
        if (typeof num === 'number') {
          return `${num}px`;
        }
        return num;
      },
    },
  };
</script>

<style lang="scss">
  .uni-forms-item {
    position: relative;
    display: flex;
    padding-top: 10px;
    /* #ifdef APP-NVUE */
    // 在 nvue 中，使用 margin-bottom error 信息会被隐藏
    padding-bottom: 22px;
    /* #endif */
    /* #ifndef APP-NVUE */
    margin-bottom: 22px;
    /* #endif */
    flex-direction: row;

    &__label {
      display: flex;
      flex-direction: row;
      align-items: center;
      text-align: left;
      font-size: 14px;
      color: #606266;
      // height: 36px;
      padding: 0 12px 0 0;
      /* #ifndef APP-NVUE */
      vertical-align: middle;
      flex-shrink: 0;
      /* #endif */

      /* #ifndef APP-NVUE */
      // box-sizing: border-box;

      /* #endif */
      &.no-label {
        padding: 0;
      }
    }
    .content-right {
      display: flex;
      justify-content: flex-end;
    }

    &__content {
      /* #ifndef MP-TOUTIAO */
      // display: flex;
      // align-items: center;
      /* #endif */
      position: relative;
      font-size: 14px;
      flex: 1;
      /* #ifndef APP-NVUE */
      box-sizing: border-box;
      /* #endif */
      flex-direction: row;

      /* #ifndef APP || H5 || MP-WEIXIN || APP-NVUE */
      // TODO 因为小程序平台会多一层标签节点 ，所以需要在多余节点继承当前样式
      & > uni-easyinput,
      & > uni-data-picker {
        width: 100%;
      }

      /* #endif */
    }

    & .uni-forms-item__nuve-content {
      display: flex;
      flex-direction: column;
      flex: 1;
    }

    &__error {
      color: #f56c6c;
      font-size: 12px;
      line-height: 1;
      padding-top: 4px;
      position: absolute;
      /* #ifndef APP-NVUE */
      top: 100%;
      left: 0;
      transition: transform 0.3s;
      transform: translateY(-100%);
      /* #endif */
      /* #ifdef APP-NVUE */
      bottom: 5px;
      /* #endif */

      opacity: 0;

      .error-text {
        // 只有 nvue 下这个样式才生效
        color: #f56c6c;
        font-size: 12px;
      }

      &.msg--active {
        opacity: 1;
        transform: translateY(0%);
      }
    }

    // 位置修饰样式
    &.is-direction-left {
      flex-direction: row;
      align-items: center;
    }

    &.is-direction-top {
      flex-direction: column;

      .uni-forms-item__label {
        padding: 0 0 8px;
        line-height: 1.5715;
        text-align: left;
        /* #ifndef APP-NVUE */
        white-space: initial;
        /* #endif */
      }
    }

    .is-required {
      // color: $uni-color-error;
      color: #dd524d;
      font-weight: bold;
      // align-self: flex-start;
      margin-right: 12rpx;
    }
  }

  .uni-forms-item--border {
    margin-bottom: 0;
    padding: 10px 0;
    // padding-bottom: 0;
    border-top: 1px #eee solid;

    /* #ifndef APP-NVUE */
    .uni-forms-item__content {
      flex-direction: column;
      justify-content: flex-start;
      align-items: flex-start;

      .uni-forms-item__error {
        position: relative;
        top: 5px;
        left: 0;
        padding-top: 0;
      }
    }

    /* #endif */

    /* #ifdef APP-NVUE */
    display: flex;
    flex-direction: column;

    .uni-forms-item__error {
      position: relative;
      top: 0px;
      left: 0;
      padding-top: 0;
      margin-top: 5px;
    }

    /* #endif */
  }

  .is-first-border {
    /* #ifndef APP-NVUE */
    border: none;
    /* #endif */
    /* #ifdef APP-NVUE */
    border-width: 0;
    /* #endif */
  }
</style>
